﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.Entity.SqlServer;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Xml;
using AlipaySDK.Dto.Payment;
using AlipaySDK.Helper;
using Common.Logging;
using Newtonsoft.Json;
using OutDbContext;
using Rmq.Center;
using User.Api.BizExtensions;
using User.Api.Dto.Dinner;
using User.Api.Helpers;

namespace User.Api.Controllers
{
    public class DinnerController : ApiController
    {
        private static readonly ILog Logger = LogManager.GetLogger(typeof(DinnerController));
        private GdsfybjyWeiXinEntities _ctx;

        private static readonly object OrderNoLocker = new object();
        private static readonly object PayNotifyLocker = new object();


        protected override void Initialize(HttpControllerContext controllerContext)
        {
            _ctx = new GdsfybjyWeiXinEntities();
            base.Initialize(controllerContext);
        }

        protected override void Dispose(bool disposing)
        {
            _ctx.Dispose();
            base.Dispose(disposing);
        }

        /// <summary>
        /// 获取菜品列表
        /// </summary>
        /// <param name="cardId"></param>
        /// <returns></returns>
        [HttpGet]
        [ActionName("GetFoodList")]
        public GetFoodListResponse GetFoodList(int cardId)
        {
            var enableDinnerCfg = ConfigurationManager.AppSettings["EnableDinner"];
            if (enableDinnerCfg != "true")
            {
                return new GetFoodListResponse
                {
                    ResultCode = "0",
                    ResultMsg = "",
                    CanDinner = false
                };
            }

            var azUser = AzUser.GetInApi(Request);
            var card = _ctx.HospCards.FirstOrDefault(x => x.CardId == cardId && x.WxUserId == azUser.UserId);
            if (card == null)
            {
                return new GetFoodListResponse
                {
                    ResultCode = "1",
                    ResultMsg = "找不到就诊卡"
                };
            }

            var wardPatient = _ctx.WardPatients.Where(
                           x => x.PatientId == card.PatientId).OrderByDescending(x => x.VisitId).FirstOrDefault();
            if (wardPatient == null)
            {
                return new GetFoodListResponse
                {
                    ResultCode = "0",
                    ResultMsg = "",
                    CanDinner = false
                };
            }

            var foodGroup =
                    _ctx.DinnerFoods.Where(
                        x =>
                            x.RecStatus == 1 &&
                            SqlFunctions.DateDiff("d", DateTime.Now, x.Date) >= 0
                            && SqlFunctions.DateDiff("n", x.Date + " " + x.HmtEndTime, DateTime.Now) <= 0
                            )
                        .OrderBy(x => x.Date).ThenBy(x => x.FpId).ThenBy(x => x.HmtId)
                        .ToList()
                        .GroupBy(x => new
                        {
                            Date = x.Date,
                            FpId = x.FpId,
                            FpName = x.FpName,
                            HmtId = x.HmtId,
                            HmtName = x.HmtName
                        }, x => x);

            var res = new GetFoodListResponse();
            res.ResultCode = "0";
            res.ResultMsg = "";
            res.CanDinner = true;
            res.Ward = wardPatient.WardName;
            res.Bed = wardPatient.BedLabel;
            res.Categories = foodGroup.Select(x =>
                 new GetFoodListResponse.FoodCategory
                 {
                     Date = x.Key.Date,
                     Day = x.Key.Date.Substring(5) + (x.Key.Date == DateTime.Today.ToString("yyyy-MM-dd") ? "(今天)"
                     : (x.Key.Date == DateTime.Today.AddDays(1).ToString("yyyy-MM-dd") ? "(明天)"
                     : (x.Key.Date == DateTime.Today.AddDays(2).ToString("yyyy-MM-dd") ? "(后天)" : ""))),
                     FpId = x.Key.FpId.GetValueOrDefault(),
                     FpName = x.Key.FpName,
                     HmtId = x.Key.HmtId.GetValueOrDefault(),
                     HmtName = x.Key.HmtName,
                     Items = x.Select(y => new GetFoodListResponse.FoodItem
                     {
                         FoodId = y.FoodId.GetValueOrDefault(),
                         FoodName = y.FoodName,
                         Price = y.FoodPrice.GetValueOrDefault(),
                         SelectedCnt = 0
                     }).ToList()
                 }
            ).ToList();

            return res;
        }

        [HttpPost]
        [ActionName("CreateDinnerOrder")]
        public CreateDinnerOrderResponse CreateDinnerOrder(CreateDinnerOrderRequest request)
        {
            var azUser = AzUser.GetInApi(Request);
            var card = _ctx.HospCards.FirstOrDefault(x => x.CardId == request.CardId && x.WxUserId == azUser.UserId);
            if (card == null)
            {
                return new CreateDinnerOrderResponse
                {
                    ResultCode = "1",
                    ResultMsg = "找不到就诊卡"
                };
            }

            var wardPatient = _ctx.WardPatients.Where(
                x => x.PatientId == card.PatientId).OrderByDescending(x => x.VisitId).FirstOrDefault();
            if (wardPatient == null)
            {
                return new CreateDinnerOrderResponse
                {
                    ResultCode = "2",
                    ResultMsg = "找不到住院信息"
                };
            }

            // 服务端验证订单
            if (request.Orders.Count <= 0)
            {
                return new CreateDinnerOrderResponse
                {
                    ResultCode = "3",
                    ResultMsg = "找不到菜品或部分菜品已下架"
                };
            }

            foreach (var order in request.Orders)
            {
                if (order.Items.Count <= 0)
                {
                    return new CreateDinnerOrderResponse
                    {
                        ResultCode = "3",
                        ResultMsg = "找不到菜品或部分菜品已下架"
                    };
                }

                foreach (var orderItem in order.Items)
                {
                    if (orderItem.Cnt <= 0)
                    {
                        return new CreateDinnerOrderResponse
                        {
                            ResultCode = "3",
                            ResultMsg = "找不到菜品或部分菜品已下架"
                        };
                    }

                    var food =
                        _ctx.DinnerFoods.FirstOrDefault(
                            x => x.RecStatus == 1 && x.Date == order.Date && x.FpId == order.FpId
                                 && x.HmtId == order.HmtId && x.FoodId == orderItem.FoodId);
                    if (food == null)
                    {
                        return new CreateDinnerOrderResponse
                        {
                            ResultCode = "3",
                            ResultMsg = "找不到菜品或部分菜品已下架"
                        };
                    }

                    order.FpName = food.FpName;
                    order.HmtName = food.HmtName;
                    order.HmtEndTime = food.HmtEndTime;

                    orderItem.FoodName = food.FoodName;
                    orderItem.UnitPrice = food.FoodPrice.GetValueOrDefault();
                    orderItem.Amount = food.FoodPrice.GetValueOrDefault() * orderItem.Cnt;
                }
            }

            var dinnerBill = new DinnerBill
            {
                OpenId = azUser.OpenId,
                BillNo = this.GenerateNewOrderNo(),
                BillStatus = DinnerExtensions.BillStatus.支付中.ToInt32(),
                TotalMoney = request.Orders.Sum(x => x.Items.Sum(y => y.Amount)),
                CreateTime = DateTime.Now,
                DeleteStatus = 0
            };
            _ctx.DinnerBills.Add(dinnerBill);
            _ctx.SaveChanges();

            foreach (var order in request.Orders)
            {
                var dinnerOrder = new DinnerOrder
                {
                    BillId = dinnerBill.Id,
                    OpenId = azUser.OpenId,
                    CardType = card.CardType,
                    CardNo = card.CardNo,
                    CardName = card.PatientName,
                    DinnerType = 0,
                    OrderDate = DateTime.Now,
                    OrderNo = this.GenerateNewOrderNo(),
                    TotalMoney = order.Items.Sum(x => x.Amount),
                    Address = "",
                    OrderStatus = DinnerExtensions.OrderStatus.未支付.ToInt32(),
                    PayStatus = DinnerExtensions.PayStatus.未付款.ToInt32(),
                    CreateTime = DateTime.Now,
                    DinnerDate = order.Date,
                    FpId = order.FpId.ToString(),
                    FpName = order.FpName,
                    HmtId = order.HmtId.ToString(),
                    HmtName = order.HmtName,
                    HmtEndTime = order.HmtEndTime,
                    FoodCount = order.Items.Sum(x => x.Cnt),
                    FoodAmount = order.Items.Sum(x => x.Amount),
                    DcBillNo = null,
                    DeleteStatus = 0,
                    BedLabel = wardPatient.BedLabel,
                    WardName = wardPatient.WardName,
                    WardExtentName = wardPatient.WardExtentName,
                    PatientName = wardPatient.Name,
                    WardCode = wardPatient.WardCode,
                    WardExtentCode = wardPatient.WardExtentCode,
                    PatientId = card.PatientId
                };

                _ctx.DinnerOrders.Add(dinnerOrder);
                _ctx.SaveChanges();

                foreach (var orderItem in order.Items)
                {
                    var dinnerOrderItem = new DinnerOrderItem
                    {
                        OrderId = dinnerOrder.Id,
                        FoodId = orderItem.FoodId.ToString(),
                        FoodName = orderItem.FoodName,
                        FoodUnitPrice = orderItem.UnitPrice,
                        FoodCount = orderItem.Cnt,
                        FoodAmount = orderItem.Amount
                    };

                    _ctx.DinnerOrderItems.Add(dinnerOrderItem);
                }
                _ctx.SaveChanges();
            }

            // RMQ 16分钟后如果还是支付中则设置支付失败
            var payingTimeoutProducer = new OrderPayingTimeoutProducer();
            payingTimeoutProducer.Publish(new OrderPayingTimeoutMsg
            {
                OrderType = OrderType.点餐订单,
                OrderId = dinnerBill.Id
            }, 16 * 60 * 1000);

            //组织 微信 js 支付方法 getBrandWCPayRequest 的参数
            var payStartTime = System.DateTime.Now.ToString("yyyyMMddHHmmss");
            var payExpireTime = System.DateTime.Now.AddMinutes(15).ToString("yyyyMMddHHmmss");

            var payFee = (dinnerBill.TotalMoney * 100).ToInt32().ToString();
            var superOpenIds = ConfigurationManager.AppSettings["OpenIdsForPayMin"]
                .Split(" , ", StringSplitOptions.RemoveEmptyEntries);
            if (superOpenIds.Contains(dinnerBill.OpenId))
            {
                payFee = "1";
            }

            //var payRequestParam = TenpayV2Helper.GenerateWxPayParameter("点餐支付", "DL" + dinnerBill.BillNo,
            //    payFee, GetClientIp(), payStartTime, payExpireTime, "Dinner/PaySucceed");
            var payRequestParam = "";

            return new CreateDinnerOrderResponse
            {
                ResultCode = "0",
                ResultMsg = "",
                BillId = dinnerBill.Id,
                BillNo = dinnerBill.BillNo,
                PayString = payRequestParam
            };
        }

        /// <summary>
        /// 用户取消支付
        /// </summary>
        /// <param name="billId">订单id</param>
        /// <returns></returns>
        [HttpGet]
        [ActionName("PayFailed")]
        public PayFailedResponse PayFailed(int billId)
        {
            var azUser = AzUser.GetInApi(Request);

            var bill = _ctx.DinnerBills.IsStatus(DinnerExtensions.BillStatus.支付中)
                .FirstOrDefault(x => x.OpenId == azUser.OpenId && x.Id == billId);
            if (bill == null)
            {
                return new PayFailedResponse
                {
                    ResultCode = "1",
                    ResultMsg = "订单不存在"
                };
            }

            bill.BillStatus = DinnerExtensions.BillStatus.支付失败.ToInt32();
            _ctx.SaveChanges();

            var orders = _ctx.DinnerOrders.Where(x => x.BillId == billId).ToList();
            foreach (var dinnerOrder in orders)
            {
                dinnerOrder.DeleteStatus = 1;
            }
            _ctx.SaveChanges();

            return new PayFailedResponse
            {
                ResultCode = "0",
                ResultMsg = ""
            };
        }

        /// <summary>
        /// 微信支付通知回调
        /// </summary>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpPost]
        [ActionName("PaySucceed")]
        public HttpResponseMessage PaySucceed()
        {
            try
            {
                Dictionary<string, string> sPara = AlipayNotifyHelper.GetRequestPost();
                string sign = HttpContext.Current.Request.Form["sign"];
                var result =
                    WapPayment.WapPayVerifyNotify(new WapPayVerifyNotifyRequest
                    {
                        SPara = sPara,
                        Sign = sign
                    });

                if (result != null && result.ResultCode == "0")
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(result.NotifyData);
                    string transactionId = xmlDoc.SelectSingleNode("/notify/trade_no")?.InnerText ?? "";
                    string out_trade_no = xmlDoc.SelectSingleNode("/notify/out_trade_no")?.InnerText;
                    string trade_status = xmlDoc.SelectSingleNode("/notify/trade_status")?.InnerText ?? "";
                    string notify_id = xmlDoc.SelectSingleNode("/notify/notify_id")?.InnerText ?? "";
                    var outTradeNo = xmlDoc.SelectSingleNode("/notify/out_trade_no")?.InnerText ?? "";
                    decimal total_fee = (xmlDoc.SelectSingleNode("/notify/total_fee")?.InnerText).ToDecimalOrDefault();
                    string buyer_id = xmlDoc.SelectSingleNode("/notify/buyer_id")?.InnerText ?? "";
                    string timeEnd = xmlDoc.SelectSingleNode("/notify/gmt_payment")?.InnerText;
                    string seller_email = xmlDoc.SelectSingleNode("/notify/seller_email")?.InnerText;

                    var isExist = false;

                    lock (PayNotifyLocker)
                    {
                        if (_ctx.WxPayNotifyLogs.Any(
                            x => x.TransactionId == transactionId && x.TradeState == 1))
                        {
                            isExist = true;
                        }
                        else
                        {
                            #region 记录通过验证的支付通知

                            //记录通知日志
                            var orderNotifyLog = new WxPayNotifyLog
                            {
                                ServiceVersion = "2.0",
                                Attach = "",
                                BankBillNo = "",
                                BankType = "AliPay",
                                BuyerAlias = "",
                                Discount = 0,
                                FeeType = 1,
                                InputCharset = "utf-8",
                                NotifyId = notify_id,
                                SignType = "0001",
                                Sign = "",//Request.Form["sign"],
                                SignKeyIndex = 0,
                                TradeMode = 1,
                                PayInfo = "",
                                //Partner = xmlDoc.SelectSingleNode("/notify/seller_id").InnerText,
                                TotalFee = (total_fee * 100).ToInt32OrDefault(),
                                TransactionId = transactionId,
                                OutTradeNo = outTradeNo,
                                //TimerEnd = xmlDoc.SelectSingleNode("/notify/gmt_payment").InnerText,
                                TransportFee = 0,
                                ProductFee = (total_fee * 100).ToInt32OrDefault(),
                                PostAppId = seller_email,
                                PostTimeStamp = timeEnd,
                                PostNonceStr = "",
                                PostOpenId = buyer_id,
                                PostAppSignature = trade_status,
                                PostIsSubscribe = "1",
                                RecCreateDt = DateTime.Now
                            };
                            if (trade_status == "TRADE_FINISHED" || trade_status == "TRADE_SUCCESS")
                                orderNotifyLog.TradeState = 1;
                            _ctx.WxPayNotifyLogs.Add(orderNotifyLog);
                            _ctx.SaveChanges();

                            #endregion
                        }
                    }

                    if (isExist)
                    {
                        return new HttpResponseMessage
                        {
                            StatusCode = HttpStatusCode.OK,
                            Content = new StringContent("success")
                        };
                    }

                    if (trade_status == "TRADE_FINISHED" || trade_status == "TRADE_SUCCESS")
                    {

                        if (outTradeNo.StartsWith("DL"))
                        {
                            var billNo = outTradeNo.Substring(2);
                            var bill = _ctx.DinnerBills.FirstOrDefault(x => x.BillNo == billNo);
                            if (bill != null && bill.BillStatus == DinnerExtensions.BillStatus.支付中.ToInt32())
                            {
                                bill.BillStatus = DinnerExtensions.BillStatus.支付成功.ToInt32();
                                bill.PayTime = DateTime.ParseExact(timeEnd, "yyyyMMddHHmmss", CultureInfo.CurrentCulture);
                                bill.WxTransactionId = transactionId;
                                _ctx.SaveChanges();

                                var orders = _ctx.DinnerOrders.Where(x => x.BillId == bill.Id).ToList();
                                foreach (var dinnerOrder in orders)
                                {
                                    dinnerOrder.PayStatus = DinnerExtensions.PayStatus.已付款.ToInt32();
                                    dinnerOrder.PayTime = DateTime.ParseExact(timeEnd, "yyyyMMddHHmmss",
                                        CultureInfo.CurrentCulture);
                                    _ctx.SaveChanges();

                                    // RMQ 执行取号操作
                                    var pickupingProducer = new OrderPickupingProducer();
                                    pickupingProducer.Publish(new OrderPickupingMsg
                                    {
                                        OrderType = Rmq.Center.OrderType.点餐订单,
                                        OrderId = dinnerOrder.Id
                                    });
                                }
                            }
                        }
                    }

                    return new HttpResponseMessage
                    {
                        StatusCode = HttpStatusCode.OK,
                        Content = new StringContent("success")
                    };
                }


                #region 记录验证失败的支付通知

                var unverifiedNotifyLog = new WxPayNotifyLog();
                //unverifiedNotifyLog.PayInfo = Request.RequestUri.AbsoluteUri;
                unverifiedNotifyLog.TradeState = 0;
                unverifiedNotifyLog.RecCreateDt = DateTime.Now;
                _ctx.WxPayNotifyLogs.Add(unverifiedNotifyLog);
                _ctx.SaveChanges();

                #endregion
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("Dinner PaySucceed Error,信息：{0}", JsonConvert.SerializeObject(ex));
            }

            return new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent("fail")
            };
        }

        /// <summary>
        /// 获取点餐状态
        /// </summary>
        /// <param name="billId"></param>
        /// <returns></returns>
        [HttpGet]
        [ActionName("GetDinnerBillStatus")]
        public GetDinnerBillStatusResponse GetDinnerBillStatus(int billId)
        {
            var azUser = AzUser.GetInApi(Request);
            var bill = _ctx.DinnerBills.FirstOrDefault(x => x.Id == billId && x.OpenId == azUser.OpenId);
            if (bill == null)
            {
                return new GetDinnerBillStatusResponse
                {
                    ResultCode = "1",
                    ResultMsg = "订单不存在"
                };
            }

            var orders = _ctx.DinnerOrders.Where(x => x.BillId == bill.Id).ToList();
            if (orders.Any(x => x.OrderStatus == DinnerExtensions.OrderStatus.未支付.ToInt32()
            || x.OrderStatus == DinnerExtensions.OrderStatus.落单中.ToInt32()))
            {
                return new GetDinnerBillStatusResponse
                {
                    ResultCode = "0",
                    ResultMsg = "",
                    DinnerStatus = "2",
                    FailedMsg = ""
                };
            }

            if (orders.All(x => x.OrderStatus != DinnerExtensions.OrderStatus.未支付.ToInt32()
            && x.OrderStatus != DinnerExtensions.OrderStatus.落单中.ToInt32()))
            {
                var succeedMsg = "";
                var succeedOrderCount = orders
                    .Count(x => x.OrderStatus == DinnerExtensions.OrderStatus.落单成功.ToInt32());
                var failedOrderCount = orders.Count() - succeedOrderCount;

                if (failedOrderCount > 0)
                {
                    succeedMsg = "点餐成功，其中有" + failedOrderCount + "失败，请注意系统发送的通知";
                }

                return new GetDinnerBillStatusResponse
                {
                    ResultCode = "0",
                    ResultMsg = "",
                    DinnerStatus = "0",
                    FailedMsg = "",
                    SucceedMsg = succeedMsg
                };
            }

            return new GetDinnerBillStatusResponse
            {
                ResultCode = "0",
                ResultMsg = "",
                DinnerStatus = "1",
                FailedMsg = ""
            };
        }

        [HttpGet]
        [ActionName("UnDinner")]
        public UnDinnerResponse UnDinner(int orderId)
        {
            var azUser = AzUser.GetInApi(Request);

            var order = _ctx.DinnerOrders.IsStatus(DinnerExtensions.OrderStatus.落单成功)
                .FirstOrDefault(x => x.OpenId == azUser.OpenId
                && x.Id == orderId);
            if (order == null)
            {
                return new UnDinnerResponse
                {
                    ResultCode = "1",
                    ResultMsg = "找不到订单"
                };
            }

            if (string.CompareOrdinal(order.DinnerDate + " " + order.HmtEndTime, DateTime.Today.ToString("yyyy-MM-dd")) <= 0)
            {
                return new UnDinnerResponse
                {
                    ResultCode = "2",
                    ResultMsg = "退餐必须在餐点结束时间之前"
                };
            }

            // RMQ 执行退餐
            var unpickupProducer = new OrderUnpickupingProducer();
            unpickupProducer.Publish(new OrderUnpickupingMsg
            {
                OrderType = OrderType.点餐订单,
                OrderId = orderId
            });

            return new UnDinnerResponse
            {
                ResultCode = "0",
                ResultMsg = ""
            };
        }

        /// <summary>
        /// 生成点餐订单号
        /// </summary>
        /// <returns></returns>
        public string GenerateNewOrderNo()
        {
            var orderNo = "AL" + DateTime.Now.Date.ToString("yyyyMMdd");
            lock (OrderNoLocker)
            {
                orderNo += (DateTime.Now - DateTime.Now.Date).TotalMilliseconds.ToString("00000000");
                Thread.Sleep(1);
            }

            return orderNo;
        }

        public string GetClientIp()
        {
            if (Request.Properties.ContainsKey("MS_HttpContext"))
            {
                string ip = System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
                //判断是否有代理服务器
                if (string.IsNullOrEmpty(ip))
                {
                    //没有代理服务器,如果有代理服务器获取的是代理服务器的IP
                    ip = System.Web.HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
                }

                string pattrn = @"(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])";
                if (!string.IsNullOrEmpty(ip) && System.Text.RegularExpressions.Regex.IsMatch(ip, pattrn))
                {
                    return ip;
                }

                return "127.0.0.1";
            }
            else
            {
                return "127.0.0.1";
            }
        }
    }
}
